﻿using Leo.Common.MQ;
using Microsoft.Extensions.DependencyInjection;

namespace Leo.Common.RabbitMQ
{
    /// <summary>
    /// Rabbit 
    /// </summary>
    public class RabbitBuilder
    {
        private readonly IServiceCollection services;

        internal readonly List<RabbitQueueInfo> Queues = new List<RabbitQueueInfo>();

        private bool disbaleConsumer;

        private readonly List<RabbitObjectBase> deleteObjects = new List<RabbitObjectBase>();

        public List<MQConsumerBase> Consumers { get; set; } = new List<MQConsumerBase>();


        internal RabbitProducer Producer { get; private set; }

        public RabbitConfig Config { get; set; }

        public RabbitConnection Connection { get; set; }


        public RabbitBuilder(RabbitConfig config, IServiceCollection services)
        {
            this.Config = config;
            this.services = services;
            this.Connection = new RabbitConnection(config);
        }


        /// <summary>
        /// 添加 消费者
        /// </summary>
        /// <typeparam name="THandler"></typeparam>
        /// <param name="queueName"></param>
        /// <returns></returns>
        public RabbitBuilder AddConsumer<THandler>(string queueName) where THandler : class, IMessageHandler
        {
            this.AddConsumer(queueName, new Type[] { typeof(THandler) });

            return this;
        }

        /// <summary>
        /// 添加 发布者
        /// </summary>
        /// <returns></returns>
        public RabbitBuilder AddProducer()
        {
            this.NewProducer();
            return this;
        }


        public IMessageQueueProvider Build()
        {
            foreach (RabbitObjectBase obj in this.deleteObjects)
            {
                this.Connection.Delete(obj);
            }


            foreach (Type type in this.Queues.SelectMany(x => x.handlerTypes.Where(p => p.HandlerInstance == null).Select(r => r.HandlerType).Distinct()))
            {
                ServiceCollectionServiceExtensions.AddSingleton(this.services, type);
            }

            RabbitProvider rabbitProvider = new RabbitProvider(this);
            ServiceCollectionServiceExtensions.AddSingleton<IMessageQueueProvider>(this.services, rabbitProvider);
            ServiceCollectionServiceExtensions.AddSingleton<RabbitMessageQueueProviderBase>(this.services, rabbitProvider);
            ServiceCollectionServiceExtensions.AddSingleton<RabbitBuilder>(this.services, this);

            if (this.Producer != null)
            {
                ServiceCollectionServiceExtensions.AddSingleton<IMQProducer>(this.services, this.Producer);
                ServiceCollectionServiceExtensions.AddSingleton<IRabbitProducer>(this.services, this.Producer);
            }

            if (!this.disbaleConsumer)
            {
                ServiceCollectionHostedServiceExtensions.AddHostedService<RabbitConsumerHostedService>(this.services);
            }
            return rabbitProvider;
        }

        /// <summary>
        /// 绑定队列
        /// </summary>
        /// <param name="binder"></param>
        /// <returns></returns>
        public RabbitBuilder BindQueue(RabbitQueueBinder binder)
        {
            this.Connection.BindQueue(binder);
            return this;
        }

        /// <summary>
        /// 申明队列
        /// </summary>
        /// <param name="participant"></param>
        /// <returns></returns>
        public RabbitBuilder Declare(RabbitObjectBase participant)
        {
            this.Connection.Declare(participant);
            RabbitQueueInfo rabbitQueueInfo = participant as RabbitQueueInfo;
            if (rabbitQueueInfo != null)
            {
                this.Queues.Add(rabbitQueueInfo);
            }
            return this;
        }

        public RabbitBuilder DisableConsumerWhen(bool predicate)
        {
            this.disbaleConsumer = predicate;
            return this;
        }


        private void AddConsumer(string queueName, params Type[] handlers)
        {
            RabbitQueueInfo rabbitQueueInfo = this.Queues.FirstOrDefault(x => x.Name == queueName);
            if (rabbitQueueInfo == null)
            {
                rabbitQueueInfo = new RabbitQueueInfo(queueName);
                this.Queues.Add(rabbitQueueInfo);
            }
            if (handlers == null || handlers.Length == 0)
            {
                return;
            }
            foreach (Type type in handlers)
            {
                rabbitQueueInfo.AddHandler(type);
            }
        }

        private void NewProducer()
        {
            if (this.Producer == null)
            {
                this.Producer = new RabbitProducer(this.Connection);
            }
        }

        public RabbitBuilder Delete(RabbitObjectBase rabbitObj)
        {
            this.deleteObjects.Add(rabbitObj);
            return this;
        }

    }
}
